package aufgabe56;

import javax.media.j3d.Alpha;
import javax.media.j3d.Appearance;
import javax.media.j3d.Background;
import javax.media.j3d.BoundingSphere;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.Canvas3D;
import javax.media.j3d.DirectionalLight;
import javax.media.j3d.Material;
import javax.media.j3d.PositionInterpolator;
import javax.media.j3d.SpotLight;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.swing.JFrame;
import javax.vecmath.Color3f;
import javax.vecmath.Point3d;
import javax.vecmath.Point3f;
import javax.vecmath.Vector3f;

import com.sun.j3d.utils.behaviors.vp.OrbitBehavior;
import com.sun.j3d.utils.geometry.Box;
import com.sun.j3d.utils.geometry.Cylinder;
import com.sun.j3d.utils.geometry.Sphere;
import com.sun.j3d.utils.picking.behaviors.PickTranslateBehavior;
import com.sun.j3d.utils.universe.SimpleUniverse;

public class Taschenlampe extends JFrame {

	private static final long serialVersionUID = -8294858318951649829L;
	private Canvas3D myCanvas3D;
	private Sphere knopf;
	public static SimpleUniverse simpUniv;
	public static BranchGroup bgLicht;
	
	public Taschenlampe() {
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		myCanvas3D = new Canvas3D(SimpleUniverse.getPreferredConfiguration());
		simpUniv = new SimpleUniverse(myCanvas3D);
		simpUniv.getViewingPlatform().setNominalViewingTransform();
		createSceneGraph(simpUniv);
		addLight(simpUniv);
		
		OrbitBehavior ob = new OrbitBehavior(myCanvas3D);
		ob.setSchedulingBounds(new BoundingSphere(new Point3d(0.0, 0.0, 0.0), Double.MAX_VALUE));
		simpUniv.getViewingPlatform().setViewPlatformBehavior(ob);
		
		setTitle("Taschenlampe");
		setSize(800, 500);
		getContentPane().add("Center", myCanvas3D);
		setVisible(true);
	}
	
	private void createSceneGraph(SimpleUniverse su) {
	    BoundingSphere bounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0), Double.MAX_VALUE);
	    
		Appearance appGrau = new Appearance();
		setToMyDefaultAppearance(appGrau, new Color3f(0.5f, 0.5f, 0.5f));
		
		BranchGroup theScene = new BranchGroup();
		
		TransformGroup	flashlight = createFlashlight();
		flashlight.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
		
		theScene.addChild(flashlight);
		
		// Wand (a)
//		Box wand = new Box(0.5f, 0.02f, 0.5f, appGrau);
//		Transform3D tfWand = new Transform3D();
//		tfWand.rotZ(Math.PI * 0.5);
//		tfWand.setTranslation(new Vector3f(1.5f, 0f, 0f));
//		TransformGroup tgWand = new TransformGroup(tfWand);
//		tgWand.addChild(wand);
		
		// Wand (b)
		TransformGroup wand = createWall();
		wand.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
		Transform3D tfWand = new Transform3D();
		tfWand.rotZ(Math.PI * 0.5);
		tfWand.setTranslation(new Vector3f(1.5f, -0.2f, -0.2f));
		TransformGroup tgWand = new TransformGroup(tfWand);
		tgWand.addChild(wand);
		
		theScene.addChild(tgWand);
		// ------
		// Kugel
		Appearance appKugel = new Appearance();
		setToMyDefaultAppearance(appKugel, new Color3f(0.0f, 0.0f, 0.0f));
		
		PickTranslateBehavior pickTrans = new PickTranslateBehavior(theScene, myCanvas3D, bounds);
		theScene.addChild(pickTrans);
		
		Sphere kugel = new Sphere(0.098f, Sphere.GENERATE_NORMALS, 100, appKugel);
		kugel.setUserData("kugel");
		kugel.setCollisionBounds(new BoundingSphere(new Point3d(0.0, 0.0, 0.0), 0.098));
		kugel.setCollidable(true);

		Transform3D tfKugel = new Transform3D();
		tfKugel.setTranslation(new Vector3f(0.15f, 0.6f, 0.0f));
		
		TransformGroup tgKugel = new TransformGroup(tfKugel);
		tgKugel.addChild(kugel);
		
		Transform3D tfKugelV = new Transform3D();
		kugel.getLocalToVworld(tfKugelV);
		
		Transform3D tfKugelInt = new Transform3D();
		tfKugelInt.rotZ(Math.PI * 1.5);
		TransformGroup tgKugelInt = new TransformGroup(tfKugelInt);
		tgKugelInt.addChild(tgKugel);
		tgKugelInt.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
		
		Transform3D tfKugelMov = new Transform3D();
		TransformGroup tgKugelMov = new TransformGroup(tfKugelMov);
		tgKugelMov.addChild(tgKugelInt);	
		
		theScene.addChild(tgKugelMov);
		
		tgKugelMov.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
		tgKugelMov.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
		tgKugelMov.setCapability(TransformGroup.ENABLE_PICK_REPORTING);
		
		Alpha alpha = new Alpha(1, 2000);
		alpha.setStartTime(Long.MAX_VALUE);

		PickingBehaviour pb = new PickingBehaviour(myCanvas3D, theScene, bounds, alpha);
		theScene.addChild(pb);

		PositionInterpolator piKugel = new PositionInterpolator(alpha, tgKugelInt, tfKugelInt, 0, 1);
		piKugel.setSchedulingBounds(bounds);
		
		theScene.addChild(piKugel);
		
		CollisionBehaviour collKugel = new CollisionBehaviour(kugel, alpha, bounds, false);
		theScene.addChild(collKugel);
		
		CollisionBehaviour collKnopf = new CollisionBehaviour(knopf, alpha, bounds, true);
		theScene.addChild(collKnopf);
		// ------
	    Background bg = new Background(new Color3f(0.35f, 0.35f, 0.35f));
	    bg.setApplicationBounds(bounds);
	    theScene.addChild(bg);

	    theScene.compile();

	    su.addBranchGraph(theScene);
	}

	private TransformGroup createFlashlight() {
		Appearance appSchwarz = new Appearance();
		setToMyDefaultAppearance(appSchwarz, new Color3f(0.0f, 0.0f, 0.0f));
		
		Appearance appGrau = new Appearance();
		setToMyDefaultAppearance(appGrau, new Color3f(0.5f, 0.5f, 0.5f));

		// Taschenlampe hinlegen
		Transform3D tfAll = new Transform3D();
		tfAll.rotZ(Math.PI * 0.5);
		TransformGroup tgAll = new TransformGroup(tfAll);
		tgAll.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
		
		// Griff
		Cylinder griff = new Cylinder(0.1f, 0.5f, appGrau);
		Transform3D tfGriff = new Transform3D();
		tfGriff.setTranslation(new Vector3f(0, 0, 0));
		TransformGroup tgGriff = new TransformGroup(tfGriff);
		tgGriff.addChild(griff);
		tgAll.addChild(tgGriff);
		
		// Deckel
		Cylinder deckel = new Cylinder(0.11f, 0.3f, appSchwarz);
		Transform3D tfDeckel = new Transform3D();
		tfDeckel.setTranslation(new Vector3f(0.0f, -0.4f, 0.0f));
		TransformGroup tgDeckel = new TransformGroup(tfDeckel);
		tgDeckel.addChild(deckel);
		tgAll.addChild(tgDeckel);
		
		// Knopf
		knopf = new Sphere(0.02f, Sphere.GENERATE_NORMALS, 100, appSchwarz);
		knopf.setUserData("knopf");
		knopf.setCollisionBounds(new BoundingSphere(new Point3d(0, -0.1, 0), 0.01f));
		knopf.setCollidable(true);
		
		Transform3D tfKnopf = new Transform3D();
		tfKnopf.setTranslation(new Vector3f(0.10f, -0.1f, 0.0f));
		TransformGroup tgKnopf = new TransformGroup(tfKnopf);
		tgKnopf.addChild(knopf);
		tgAll.addChild(tgKnopf);
		
		// Scheibe
		Cylinder scheibe = new Cylinder(0.1f, 0.1f, appGrau);
		Transform3D tfScheibe = new Transform3D();
		tfScheibe.setTranslation(new Vector3f(0.0f, -0.5001f, 0.0f));
		TransformGroup tgScheibe = new TransformGroup(tfScheibe);
		tgScheibe.addChild(scheibe);
		tgAll.addChild(tgScheibe);
		
		return tgAll;
	}
	
	private TransformGroup createWall() {
		Appearance appGrau = new Appearance();
		setToMyDefaultAppearance(appGrau, new Color3f(0.5f, 0.5f, 0.5f));
		
		TransformGroup tgAll = new TransformGroup();
		
		for (int i = 0; i < 25; i++) {
			Box wand = new Box(0.1f, 0.02f, 0.1f, appGrau);
			Transform3D tfWand = new Transform3D();
			tfWand.setTranslation(new Vector3f((float)((i % 5) * 0.1), 0f, (float)(((int)(i / 5)) * 0.1)));
			TransformGroup tgWand = new TransformGroup(tfWand);
			tgWand.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
			tgWand.addChild(wand);
			tgAll.addChild(tgWand);
		}
		
		return tgAll;
	}
	
	private void addLight(SimpleUniverse su) {
		bgLicht = new BranchGroup();
		bgLicht.setCapability(BranchGroup.ALLOW_DETACH);
		bgLicht.setUserData("licht");
		
		BranchGroup licht = new BranchGroup();
		BoundingSphere bounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0),	100.0);
		
		Color3f lichtFarbe = new Color3f(0f, 0f, 1f);
		
		SpotLight lichtLampe = new SpotLight(lichtFarbe, new Point3f(0.7f, 0.0f, 0.0f), new Point3f(0.1f, 0.1f, 0.01f), new Vector3f(-1.0f, 0.0f, 0.0f), (float) (Math.PI / 15), 0.0f);
		lichtLampe.setUserData("lichtLampe");
		lichtLampe.setInfluencingBounds(bounds);
		
		SpotLight lichtWand = new SpotLight(lichtFarbe, new Point3f(0.51f,	0.0f, 0.0f), new Point3f(0.1f, 0.1f, 0.01f), new Vector3f(1.0f, 0.0f, 0.0f), (float) (Math.PI / 15), 0.0f);
		lichtWand.setUserData("lichtWand");
		lichtWand.setInfluencingBounds(bounds);
		
		Vector3f lichtRichtung = new Vector3f(-1.0f, 0.0f, -0.5f);
		DirectionalLight light1 = new DirectionalLight(lichtFarbe, lichtRichtung);
		light1.setInfluencingBounds(bounds);
		
		licht.addChild(lichtLampe);
		licht.addChild(lichtWand);
		
		bgLicht.addChild(licht);
		su.addBranchGraph(bgLicht);
	}

	private static void setToMyDefaultAppearance(Appearance app, Color3f col) {
		app.setMaterial(new Material(col, col, col, col, 150.0f));
	}
	
	public static void main(String[] args) {
		new Taschenlampe();
	}
}
